package com.dylan.channel.v11.channel;

import com.dylan.channel.v11.EventLoop;
import com.dylan.channel.v11.attribute.DefaultAttributeMap;
import com.dylan.channel.v11.future.ChannelFuture;
import com.dylan.channel.v11.future.ChannelPromise;
import com.dylan.channel.v11.handler.ChannelPipeline;
import com.dylan.channel.v11.handler.DefaultChannelPipeline;

import java.net.SocketAddress;

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
    private volatile EventLoop eventLoop;
    /**
     * 如果是服务端channel则parent为null
     */
    private final Channel parent;

    /**
     * @Author: PP-jessica
     * @Description:加入unsafe属性了
     */
    private final Unsafe unsafe;

    /**
     * @Author: PP-jessica
     * @Description:添加DefaultChannelPipeline属性
     */
    private final DefaultChannelPipeline pipeline;

    /**
     * 是否注册完成
     */
    private volatile boolean registered;

    /**
     * @Author: PP-jessica
     * @Description:该方法的位置正确，但是参数和源码有差异，源码的参数是netty自己定义的ChannelOutboundBuffer，是出站的数据缓冲区 现在，我们先用最简单的实现，之后再重写
     */
    protected abstract void doWrite(Object msg) throws Exception;

    protected AbstractChannel(Channel parent) {
        unsafe = newUnsafe();
        this.parent = parent;
        this.pipeline = newChannelPipeline();
    }

    protected DefaultChannelPipeline newChannelPipeline() {
        //把创建出的channel传入DefaultChannelPipeline；
        return new DefaultChannelPipeline(this);
    }

    @Override
    public Unsafe unsafe() {
        return unsafe;
    }

    protected abstract AbstractUnsafe newUnsafe();

    @Override
    public EventLoop eventLoop() {
        EventLoop eventLoop = this.eventLoop;
        if (eventLoop == null) {
            throw new IllegalStateException("channel not registered to an event loop");
        }
        return eventLoop;
    }

    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
        unsafe.connect(remoteAddress, localAddress, promise);
        return promise;
    }

    @Override
    public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return pipeline.bind(localAddress, promise);
    }

    @Override
    public boolean isRegistered() {
        return registered;
    }

    @Override
    public ChannelFuture newFailedFuture(Throwable cause) {
        return null;
    }

    @Override
    public ChannelFuture newSucceededFuture() {
        return null;
    }

    @Override
    public ChannelPromise newPromise() {
        return null;
    }

    @Override
    public void beginRead() {
        doBeginRead();
    }

    protected abstract void doBeginRead();

    @Override
    public Channel read() {
        unsafe.beginRead();
        return this;
    }

    @Override
    public ChannelPipeline pipeline() {
        return this.pipeline;
    }

    protected void doRegister() throws Exception {
    }

    protected abstract void doBind(SocketAddress localAddress) throws Exception;

    protected abstract class AbstractUnsafe implements Unsafe {
        private void assertEventLoop() {
            assert !registered || eventLoop.inEventLoop(Thread.currentThread());
        }

        @Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            try {
                doBind(localAddress);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * @Author: PP-jessica
         * @Description:这个方法在上一节课没有实现，在这一节课，我们也不会真正的实现它，但是可以在这里稍微加点东西，讲解一下发送消息的逻辑和步骤 ByteBuf讲完了之后，我们再重写该方法。在这里，该方法只是把数据发送到了一个netty自定义的缓冲区中，还没有放入socket的缓冲区
         * 真正的write方法在子类NioSocketChannel中实现，在那个方法中，数据才被真正放进socket的缓冲区中
         * 根据之前的老套路，仍需要在AbstractChannel抽象类中定义一个抽象方法，让子类去实现
         */
        @Override
        public final void write(Object msg, ChannelPromise promise) {
            try {
                doWrite(msg);
                //如果有监听器，这里可以通知监听器执行回调方法
                promise.setSuccess();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * @Author: PP-jessica
         * @Description:在源码中，如果调用的是writeAndFlush方法发送数据，在write方法执行后，会紧接着执行flush方法 这个我们也会在后面讲到
         */
        @Override
        public final void flush() {
        }

        @Override
        public final void beginRead() {
            assertEventLoop();
            //如果是服务端的channel，这里仍然可能为false
            //那么真正注册读事件的时机，就成了绑定端口号成功之后
//            if (!isActive()) {
//                return;
//            }
            try {
                doBeginRead();
            } catch (final Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }


        private void register0(ChannelPromise promise) {
            try {
                //开始执行真正得注册逻辑
                doRegister();

                registered = true;

                //开始注册自己感兴趣得事件
                beginRead();

                //将异步结果设置成成功
                promise.setSuccess();
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }

        @Override
        public void register(EventLoop eventLoop, ChannelPromise promise) {
            //断言eventLoop是否为空
            assert eventLoop != null;

            //断言未注册过
            assert !isRegistered();
            //稍微学过netty的人都知道，一个channel绑定一个单线程执行器。终于在这里，我们看到channel绑定了单线程执行器
            //接着channel，不管是客户端还是服务端的，会把自己注册到绑定的单线程执行器中的selector上
            AbstractChannel.this.eventLoop = eventLoop;
            if (eventLoop.inEventLoop(Thread.currentThread())) {
                register0(promise);
            } else {
                eventLoop.execute(new Runnable() {
                    @Override
                    public void run() {
                        register0(promise);
                    }
                });
            }
        }
    }
}
